/****************************************Copyright (c)*************************
**                               版权所有 (C), 2020-2030, 零匙科技
**
**                                 https://www.0k.com/
**
**--------------文件信息-------------------------------------------------------
**文   件   名: leokey_ble_protocol.c
**描        述: 下发/上报数据处理函数
**使 用 说 明 :

                  *******非常重要，一定要看哦！！！********

** 1、用户在此文件中实现数据下发/上报功能
** 2、当开始某些宏定义后需要用户实现代码的函数内部有#err提示,完成函数后请删除该#err
**
**--------------当前版本修订---------------------------------------------------
** 版  本: v1.0.0
** 日　期: 2022年02月15日
** 描　述: 1:协议初版
** 作  者：zouchuan
**-----------------------------------------------------------------------------
******************************************************************************/

#include "leokey_ble.h"
#include "driver_uart.h"
#include "hal_debug.h"

/******************************************************************************
                                移植须知:
1:MCU必须在while中直接调用mcu_api.c内的leokey_ble_uart_service()函数
2:程序正常初始化完成后,建议不进行关串口中断,如必须关中断,关中断时间必须短,关中断会引起串口数据包丢失
3:请勿在中断/定时器中断内调用上报函数
******************************************************************************/


/******************************************************************************
                              第一步:初始化
1:在需要使用到leokey_ble相关文件的文件中include "leokey_ble.h"
2:在MCU初始化中调用mcu_api.c文件中的leokey_ble_protocol_init()函数
3:将MCU串口单字节发送函数填入protocol.c文件中uart_transmit_output函数内,并删除#error
4:在MCU串口接收函数中调用mcu_api.c文件内的uart_receive_input函数,并将接收到的字节作为参数传入
5:单片机进入while循环后调用mcu_api.c文件内的leokey_ble_uart_service()函数
******************************************************************************/



/******************************************************************************
                           2:串口单字节发送函数
请将MCU串口发送函数填入该函数内,并将接收到的数据作为参数传入串口发送函数
******************************************************************************/

/*****************************************************************************
函数名称 : uart_transmit_output
功能描述 : 发数据处理
输入参数 : value:串口收到字节数据
返回参数 : 无
使用说明 : 请将MCU串口发送函数填入该函数内,并将接收到的数据作为参数传入串口发送函数
*****************************************************************************/
void uart_transmit_output(unsigned char value)
{
    //#error "4、请将MCU串口发送函数填入该函数,并删除该行"
        
    /*
      //示例:
      extern void Uart_PutChar(unsigned char value);
      Uart_PutChar(value);	                                //串口发送函数
    */
    
    driver_uart1_send_byte(value);

}

/******************************************************************************
                           第二步:实现具体用户函数
1:APP下发数据处理
******************************************************************************/
static unsigned char ble_download_open_lock_handle(void);
static unsigned char ble_download_get_lock_status_handle(void);
static unsigned char ble_download_get_lock_info_handle(void);
static unsigned char ble_download_get_log_num_handle(void);
static unsigned char ble_download_get_log_data_handle(unsigned int num);


static unsigned char ble_download_get_firmware_info_handle(void);
static unsigned char ble_download_firmware_confirm_handle(const unsigned char * buf);
static unsigned char ble_download_erase_store_handle(void);
static unsigned char ble_download_enter_updata_handle(void);
static unsigned char ble_download_package_transfer_handle(const unsigned char * buf);
static unsigned char ble_download_firmware_verify_handle(void);
static unsigned char ble_download_load_firmware_handle(void);


uint16_t C_check = 0, R_check = 0;
uint32_t package_size = 0, C_package_size = 0;
uint16_t software_version = 0;
static uint16_t Calc_CRC16(uint8_t *buf, uint16_t len, uint16_t crc);
static unsigned char leokey_ble_check_ota_byte(unsigned char *data, unsigned char check);


unsigned char ble_cmd_data_handle(const unsigned char* data_buf, unsigned short len)
{
    unsigned char ble_cmd = data_buf[0];
    
    switch (ble_cmd)
    {
        //0x00：通用回复
        case 0x00:    
            LeoKey_Printf("leokey ble wakeup \r\n");
            break;
            
        //0x50：开锁
        case 0x50:      
            ble_download_open_lock_handle();
            break;   
        
        //0x30：获取锁状态
        case 0x30:      
            ble_download_get_lock_status_handle();
            break;   
        
        //0x31：获取锁信息
        case 0x31:      
            ble_download_get_lock_info_handle();
            break;  
        
        //0x32：获取日志数量
        case 0x32:      
            ble_download_get_log_num_handle();
            break;   
        
        //0x33：获取日志数据
        case 0x33:      
            ble_download_get_log_data_handle(byte_to_int(data_buf+1));
            break;     

        //0x40：获取固件信息
        case 0x40:      
            ble_download_get_firmware_info_handle();
            break; 

        //0x41：固件确认
        case 0x41:      
            ble_download_firmware_confirm_handle(data_buf+1);
            break; 
        
        //0x43：擦除固件存储区域
        case 0x43:      
            ble_download_erase_store_handle();
            break;  

        //0x44：进入升级模式
        case 0x44:      
            ble_download_enter_updata_handle();
            break; 

        //0x45：升级包传输
        case 0x45:      
            ble_download_package_transfer_handle(data_buf+1);
            break;   

        //0x46：检查内容完整性
        case 0x46:      
            ble_download_firmware_verify_handle();
            break;    

        //0x47：加载新固件
        case 0x47:      
            ble_download_load_firmware_handle();
            break;  
        
        //0x51：连接成功
        case 0x51:      
            LeoKey_Printf("leokey ble 0x51 connect\r\n");
            break;   
        
        //0x52：连接断开
        case 0x52:      
            LeoKey_Printf("leokey ble 0x52 disconnect\r\n");
            break;   
        
        //0x53：开始广播
        case 0x53:      
            LeoKey_Printf("leokey ble 0x53 adv start\r\n");
            break;   
        
        //0x54：广播空闲
        case 0x54:      
            LeoKey_Printf("leokey ble 0x54 adv stop\r\n");
            break;   

        //0x55：模块休眠
        case 0x55:      
            LeoKey_Printf("leokey ble 0x55 sleep\r\n");
            break;         
 
        //0x56：蓝牙已添加成功
        case 0x56:      
            LeoKey_Printf("leokey ble add app success \r\n");
            break;  
        
        //0x57：蓝牙已恢复出厂
        case 0x57:      
            LeoKey_Printf("leokey ble factory reset success \r\n");
            break;          
        default:
            return 0;
    }
    return 0;
}



unsigned char ble_master_query_info(const unsigned char* data_buf, unsigned short len)
{
    unsigned char ble_cmd = data_buf[0];
    
    switch (ble_cmd)
    {
        //0x01：蓝牙模块MAC地址
        case 0x01:    
            LeoKey_Printf("leokey ble mac ");
            Hexdump_Printf((uint8_t *)data_buf+1 , 6, ' '); 
            break;
            
        //0x02：绑定乐开APP的UserKey
        case 0x02:      
            LeoKey_Printf("leokey app user key ");
            Hexdump_Printf((uint8_t *)data_buf+1 , 16, ' '); 
            break;   
        
        //0x03：蓝牙模块版本号
        case 0x03:      
            LeoKey_Printf("leokey ble version ");
            Hexdump_Printf((uint8_t *)data_buf+1 , 8,' '); 

            break;  

        //0xA1：清空蓝牙出厂信息
        case 0xA1:      
            LeoKey_Printf("clear leokey ble factory info ");
            Hexdump_Printf((uint8_t *)data_buf+1, 2, ' '); 
            if((0 != data_buf[1])&&(0 != data_buf[2]))
            {
                LeoKey_Printf("Factory info clear success");
            }
            else
            {
                LeoKey_Printf("Factory info clear failed");
            }

            break;           
        
        default:
            return 0;
    }
    return 0;
}

/*****************************************************************************
函数名称 : ble_download_open_lock_handle
功能描述 : 开锁
输入参数 : 无
返回参数 : 成功返回:SUCCESS/失败返回:ERROR
使用说明 : 
*****************************************************************************/
static unsigned char ble_download_open_lock_handle(void)
{
    
    unsigned short length = 0;

    LeoKey_Printf("open lock \r\n");
       
    length = set_leokey_ble_uart_byte(length, 0x50);    

    leokey_ble_uart_write_frame(LEOKEY_BLE_CFG_CMD, length);
    
    // 可在此处执行开锁动作
    
    return SUCCESS;
}




/*****************************************************************************
函数名称 : ble_download_get_lock_status_handle
功能描述 : 获取锁状态
输入参数 : 无  
返回参数 : 成功返回:SUCCESS/失败返回:ERROR
使用说明 : 
*****************************************************************************/
static unsigned char ble_download_get_lock_status_handle(void)
{

    unsigned short length = 0;

    LeoKey_Printf("get_lock_status \r\n");
    
    length = set_leokey_ble_uart_byte(length, 0x30);
    
    // [1 字节] 电池电量
    length = set_leokey_ble_uart_byte(length, 80);
    
    // [1 字节] 锁开关状态 
    length = set_leokey_ble_uart_byte(length, 0x08);
    
    // [1 字节] 锁音量
    length = set_leokey_ble_uart_byte(length, 0x01);
    
    // [1 字节] 锁使用状态
    length = set_leokey_ble_uart_byte(length, 0x02);

    leokey_ble_uart_write_frame(LEOKEY_BLE_CFG_CMD, length);
    
    return SUCCESS;
}



/*****************************************************************************
函数名称 : ble_download_get_lock_info_handle
功能描述 : 获取锁信息
输入参数 : 无
返回参数 : 成功返回:SUCCESS/失败返回:ERROR
使用说明 : 
*****************************************************************************/
static unsigned char ble_download_get_lock_info_handle(void)
{
    unsigned short length = 0;

    LeoKey_Printf("get_lock_info \r\n");
    
    length = set_leokey_ble_uart_byte(length, 0x31);
    
    // [2 字节] 锁设备型号
    length = set_leokey_ble_uart_byte(length, 0x20);
    length = set_leokey_ble_uart_byte(length, 0x01);
    
    // [2 字节] 锁硬件版本 
    length = set_leokey_ble_uart_byte(length, 0x02);
    length = set_leokey_ble_uart_byte(length, 0x03);
    
    // [2 字节] 锁软件版本
    length = set_leokey_ble_uart_byte(length, 0x61);
    length = set_leokey_ble_uart_byte(length, 0xC5);
    
    // [1 字节] 锁功能属性
    length = set_leokey_ble_uart_byte(length, 0x15);

    leokey_ble_uart_write_frame(LEOKEY_BLE_CFG_CMD, length);
    
    return SUCCESS;
    
}



/*****************************************************************************
函数名称 : ble_download_get_log_num_handle
功能描述 : 获取日志数量
输入参数 : 无
返回参数 : 成功返回:SUCCESS/失败返回:ERROR
使用说明 : 
*****************************************************************************/
static unsigned char ble_download_get_log_num_handle(void)
{
    unsigned short length = 0;

    LeoKey_Printf("get_lock_num \r\n");
    
    length = set_leokey_ble_uart_byte(length, 0x32);
    
    // [4 字节] 锁日志起始编号
    length = set_leokey_ble_uart_byte(length, 0x00);
    length = set_leokey_ble_uart_byte(length, 0x00);  
    length = set_leokey_ble_uart_byte(length, 0x00);
    length = set_leokey_ble_uart_byte(length, 0x00);
    
    // [4 字节] 锁日志结束编号
    length = set_leokey_ble_uart_byte(length, 0x00);
    length = set_leokey_ble_uart_byte(length, 0x00);
    length = set_leokey_ble_uart_byte(length, 0x03);
    length = set_leokey_ble_uart_byte(length, 0x60);


    leokey_ble_uart_write_frame(LEOKEY_BLE_CFG_CMD, length);
    
    return SUCCESS;
}



/*****************************************************************************
函数名称 : ble_download_get_log_data_handle
功能描述 : 获取锁板的日志数据
输入参数 : 要获取第几条日志(包含第0条)
返回参数 : 成功返回:SUCCESS/失败返回:ERROR
使用说明 : 
*****************************************************************************/
static unsigned char ble_download_get_log_data_handle(unsigned int num)
{
    unsigned short length = 0;
    unsigned char  log_buf[16];
    
    
    LeoKey_Printf("get_log_data, num: %d \r\n", (int)num);
    
    /*
        此处添加从存储区域中读取的日志的数据并放入缓冲区log_buf中
    */
    
    
    // 以下为模拟的假数据。日志存储格式可参考下面的假数据。
    
    log_buf[0] = 0x01;  //日志类型: 00-开锁日志, 01-异常日志, 02-钥匙日志
    
    log_buf[1] = timestamp[0];  //UTC时间戳
    log_buf[2] = timestamp[1];
    log_buf[3] = timestamp[2];
    log_buf[4] = timestamp[3];
    
       
    switch (log_buf[0])
    {
        //0x00-开锁日志
        case 0x00:    
            log_buf[5] = 1;      //钥匙编号
            log_buf[6] = 00;     //钥匙类型
            log_buf[7] = 80;     //设备百分比电量
            log_buf[8] = 0;      //密码暗码长度，不用时，填充0                         
            my_memset(log_buf + 9, 0, 6);       
            break;
            
        //0x01-异常日志
        case 0x01:      
            log_buf[5] = 1;  //异常类型
            my_memset(log_buf + 6, 0, 9);
            break;
        
        //0x02-钥匙日志
        case 0x02:      
            log_buf[5] = 1;      //钥匙编号
            log_buf[6] = 1;      //钥匙类型
            log_buf[7] = 0;      //添加还是删除, 0-添加; 1-删除
            log_buf[8] = 0;      //密码暗码长度，不用时，填充0
            my_memset(log_buf + 9, 0, 6);
            break;
        
        default:
            break;
    }
    
    
    
    
    
    // 0x33 - 获取成功 , 0x01 - 编号不存在
    length = set_leokey_ble_uart_byte(length, 0x33);
      
    length = set_leokey_ble_uart_buffer(length, log_buf, 16);

    leokey_ble_uart_write_frame(LEOKEY_BLE_CFG_CMD, length);
    
    return SUCCESS;
}







#ifdef SUPPORT_MCU_RTC_CHECK

/*****************************************************************************
函数名称 : ble_write_mcu_rtctime
功能描述 : BLE校对本地MCU_RTC时钟
输入参数 : 无
返回参数 : 无
使用说明 : MCU需要自行实现该功能
*****************************************************************************/
unsigned char ble_write_mcu_rtctime(unsigned char time[])
{
    //#error "请自行完成RTC时钟写入代码,并删除该行"
    unsigned short length = 0;
    
    
    my_memcpy((void *) timestamp, (const char *) time, 4);    //获取时间戳
    leokey_ble_timestamp_to_time();    //获取时间计算结果
    
    // 例: 2022/3/8 15:23:49
    LeoKey_Printf("%d/%d/%d %d:%d:%d \r\n", _time.w_year, _time.w_month, \
                _time.w_date, _time.hour, \
                _time.min, _time.sec);
       
    
 
    // 回复蓝牙模块
    length = set_leokey_ble_uart_byte(length, 0x00);
    
    leokey_ble_uart_write_frame(SET_TIMESTAMP_CMD, length); // SET_TIMESTAMP_CMD
    
    return SUCCESS;
}

#endif




#ifdef SUPPORT_MCU_FIRM_UPDATE

/*****************************************************************************
函数名称 : ble_download_get_firmware_info_handle
功能描述 : 获取固件信息
输入参数 : 无
返回参数 : 成功返回:SUCCESS/失败返回:ERROR
使用说明 : 
*****************************************************************************/
static unsigned char ble_download_get_firmware_info_handle(void)
{
    unsigned short length = 0;

    LeoKey_Printf("get_firmware_info \r\n");
    
    length = set_leokey_ble_uart_byte(length, 0x40);
    
    // [2 字节] 锁设备型号
    length = set_leokey_ble_uart_byte(length, 0x20);
    length = set_leokey_ble_uart_byte(length, 0x01);
          
    // [1 字节] reserver
    length = set_leokey_ble_uart_byte(length, 0x01);
    
    // [2 字节] 锁硬件版本 
    length = set_leokey_ble_uart_byte(length, 0x02);
    length = set_leokey_ble_uart_byte(length, 0x03);
    
    // [2 字节] 锁软件版本
    length = set_leokey_ble_uart_byte(length, 0x61);
    length = set_leokey_ble_uart_byte(length, 0xC5);
    


    leokey_ble_uart_write_frame(LEOKEY_BLE_CFG_CMD, length);
    
    return SUCCESS;
    
}


/*****************************************************************************
函数名称 : ble_download_firmware_confirm_handle
功能描述 : 确认固件信息
输入参数 : *buf 
返回参数 : 成功返回:SUCCESS/失败返回:ERROR
使用说明 : 
*****************************************************************************/
static unsigned char ble_download_firmware_confirm_handle(const unsigned char * buf)
{
    unsigned short length = 0;

    LeoKey_Printf("confirm_firmware_info \r\n");
    
    software_version = byte_to_short(buf);
    package_size = (unsigned int)byte_to_int(buf+2);
    R_check =  byte_to_short(buf+6);
    
    LeoKey_Printf("software version 0x%x \r\n", software_version);
    LeoKey_Printf("updata package size [%d] Byte\r\n", package_size);
    LeoKey_Printf("package crc16 0x%x \r\n", R_check);
    
    // 上面是收到待升级的软件版本号，确定是否满足升级需要。

    
    length = set_leokey_ble_uart_byte(length, 0x41);
    
    // [1 字节] 0x00~0x06
    length = set_leokey_ble_uart_byte(length, 0x00);   

    leokey_ble_uart_write_frame(LEOKEY_BLE_CFG_CMD, length);
    
    return SUCCESS;
    
}


/*****************************************************************************
函数名称 : ble_download_erase_store_handle
功能描述 : 擦除Flash
输入参数 : 无 
返回参数 : 成功返回:SUCCESS/失败返回:ERROR
使用说明 : 
*****************************************************************************/
static unsigned char ble_download_erase_store_handle(void)
{
    unsigned short length = 0;

    LeoKey_Printf("erase store \r\n");
    
    // 擦除存储固件的区域，具体逻辑MCU根据自身方案来定。
    
    
    length = set_leokey_ble_uart_byte(length, 0x43);
    
    // [1 字节] 0x00~0x02
    length = set_leokey_ble_uart_byte(length, 0x00);   

    leokey_ble_uart_write_frame(LEOKEY_BLE_CFG_CMD, length);
    
    return SUCCESS;
    
}


/*****************************************************************************
函数名称 : ble_download_enter_updata_handle
功能描述 : MCU进入升级模式
输入参数 : 无 
返回参数 : 成功返回:SUCCESS/失败返回:ERROR
使用说明 : 
*****************************************************************************/
static unsigned char ble_download_enter_updata_handle(void)
{
    unsigned short length = 0;

    LeoKey_Printf("enter updata \r\n");
    
    // MCU进入升级模式，具体逻辑MCU根据自身方案来定。
    
    
    length = set_leokey_ble_uart_byte(length, 0x44);
    
    // [1 字节] 0x00~0x02
    length = set_leokey_ble_uart_byte(length, 0x00);   

    leokey_ble_uart_write_frame(LEOKEY_BLE_CFG_CMD, length);
    
    return SUCCESS;
    
}



/*****************************************************************************
函数名称 : ble_download_package_transfer_handle
功能描述 : 固件传输
输入参数 : *buf 
返回参数 : 成功返回:SUCCESS/失败返回:ERROR
使用说明 : 
*****************************************************************************/
static unsigned char ble_download_package_transfer_handle(const unsigned char * buf)
{
    unsigned short int length = 0;
    unsigned short int package_num = 0;
    unsigned short int data_length = 0;
    uint8_t crc_ret = FALSE, ret = 0;

    
    package_num = byte_to_short(buf);
    data_length = buf[2];

    LeoKey_Printf("package_transfer package_num [%d] ,data_length[%d]\r\n", package_num, data_length );
    
    
    // 上面是收到的每一包。
    C_package_size += data_length;
    
    // 对每一包做校验
    crc_ret = leokey_ble_check_ota_byte((unsigned char *)buf, TRUE);
            
    if (crc_ret)
    {
        LeoKey_Printf("package_num [%d] verify success\r\n", package_num);
        
        C_check = Calc_CRC16((uint8_t *)buf+3, data_length, C_check);
        
        package_num++;
        ret = 0;
    }
    else
    {
        // 若校验错误，package_num不会++，还会请求这一包数据。
        LeoKey_Printf("package_num [%d] verify error\r\n", package_num);
        
        ret = 1;
        
    }
    
    length = set_leokey_ble_uart_byte(length, 0x45);
    
    // [1 字节] 0x00~0x02
    length = set_leokey_ble_uart_byte(length, ret);   
    
    // [2 字节] 期望下一帧包编号

    length = set_leokey_ble_uart_byte(length, (uint8_t)(package_num>>8)); 
    length = set_leokey_ble_uart_byte(length, (uint8_t)(package_num & 0x00FF)); 

    leokey_ble_uart_write_frame(LEOKEY_BLE_CFG_CMD, length);
    
    return SUCCESS;
    
}


/*****************************************************************************
函数名称 : ble_download_firmware_verify_handle
功能描述 : 固件信息校验
输入参数 : 无  
返回参数 : 成功返回:SUCCESS/失败返回:ERROR
使用说明 : 
*****************************************************************************/
static unsigned char ble_download_firmware_verify_handle(void)
{
    unsigned short length = 0;
    
    // 如果计算出来的校验值和命令发送过来的校验值相同
    // 且接收到的固件大小和命令告知的固件大小也相同
    if((C_package_size == package_size) && (C_check == R_check))
    {   
        LeoKey_Printf("firmware verify success\r\n");
    }
    else
    {
        LeoKey_Printf("firmware verify error\r\n");
    }
    
    LeoKey_Printf("C_package_size [%d] \r\n", C_package_size);
    LeoKey_Printf("C_check [0x%x] \r\n", C_check);
    
    C_package_size = 0;
    C_check = 0;
       
    length = set_leokey_ble_uart_byte(length, 0x46);
    
    // [1 字节] 0x00~0x01
    length = set_leokey_ble_uart_byte(length, 0x00);
    

    leokey_ble_uart_write_frame(LEOKEY_BLE_CFG_CMD, length);
    
    return SUCCESS;
}


/*****************************************************************************
函数名称 : ble_download_load_firmware_handle
功能描述 : 加载新固件
输入参数 : 无  
返回参数 : 成功返回:SUCCESS/失败返回:ERROR
使用说明 : 
*****************************************************************************/
static unsigned char ble_download_load_firmware_handle(void)
{

    unsigned short length = 0;

    LeoKey_Printf("load firmware \r\n");
    
    
       
    length = set_leokey_ble_uart_byte(length, 0x47);
    
    // [1 字节] 0x00
    length = set_leokey_ble_uart_byte(length, 0x00);
    

    leokey_ble_uart_write_frame(LEOKEY_BLE_CFG_CMD, length);
    
    return SUCCESS;
}





#define OTA_FRAME_LEN               240



// 240字节一包的OTA数据校验
static unsigned char leokey_ble_check_ota_byte(unsigned char *data, unsigned char check)
{
    uint8_t checkByte = *data;
    for (uint8_t i = 1; i < (OTA_FRAME_LEN - 1); i++)
    {
        checkByte ^= *(data + i);
    }
    if (check)
    {
        return (checkByte == *(data + (OTA_FRAME_LEN - 1)));
    }
    else
    {
        *(data + (OTA_FRAME_LEN - 1)) = checkByte;
        return 0;
    }
}





static uint16_t Calc_CRC16(uint8_t *buf, uint16_t len, uint16_t crc)
{
    int i;
    for (; len > 0; len--) /* Step through bytes in memory */
    {
        crc = crc ^ (*buf++ << 8); /* Fetch byte from memory, XOR into CRC top byte*/
        for (i = 0; i < 8; i++)    /* Prepare to rotate 8 bits */
        {
            if (crc & 0x8000)              /* b15 is set... */
                crc = (crc << 1) ^ 0x1021; /* rotate and XOR with polynomic */
            else                           /* b15 is clear... */
                crc <<= 1;                 /* just rotate */
        }                                  /* Loop for 8 bits */
        crc &= 0xFFFF;                     /* Ensure CRC remains 16-bit value */
    }                                      /* Loop until num=0 */
    return (crc);                          /* Return updated CRC */
}


#endif
